package com.xinziruo.richclient.handler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonNode;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.DomSerializer;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.Utils;
import org.w3c.dom.Document;

import com.xinziruo.richclient.engine.CrawlResponse;
import com.xinziruo.richclient.util.XPathUtils;

public class CrawlParser {
	private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
	private static HtmlCleaner htmlCleaner;

	static {

		CleanerProperties props = new CleanerProperties();
		props.setNamespacesAware(false);
		props.setOmitComments(true);
		props.setUseCdataForScriptAndStyle(false);
		htmlCleaner = new HtmlCleaner(props);

	}

	/**
	 * 
	 * @param enterUrl
	 * @param doc
	 * @param json
	 * @return
	 */
	public static Map<String, Object> parse(String enterUrl, Object doc, JsonNode json) {

		Map<String, Object> result = new HashMap<String, Object>();
		
		for (JsonNode node : json.path("parseExpressions")) {
			try {

				String name = node.path("name").getTextValue();
				String expression = node.path("expression").getTextValue();
				String resultType = node.path("resultType").getTextValue();

				List<String> values = XPathUtils.evalAsStringList(doc, expression);
				if (values.isEmpty()) {
					result.put(name, "");
					continue;
				}

				if (values.size() > 1) {
					result.put(name, convertValuesIfNeccessary(node, values, enterUrl, resultType));
					continue;
				}

				result.put(name, convertValueIfNeccessary(node, values.get(0), enterUrl, resultType));
			} catch (Exception e) {
				// ingore this exception
			}

		}

		return result;
	}

	private static List<String> convertValuesIfNeccessary(JsonNode node, List<String> values, String enterUrl, String resultType) {

		for (int i = 0; i < values.size(); i++) {
			values.set(i, convertValueIfNeccessary(node, values.get(i), enterUrl, resultType));
		}

		return values;
	}

	private static String convertValueIfNeccessary(JsonNode node, String value, String enterUrl, String resultType) {

		for (JsonNode stringFilter : node.path("stringFilterConfigs")) {
			
			value = value.replaceAll(
											stringFilter.path("regex").getTextValue(),
											stringFilter.path("replacement").getTextValue()
										);
		}

		if (isLinkResultType(resultType)) {
			return Utils.fullUrl(enterUrl, value);
		}

		return value;
	}

	private static boolean isLinkResultType(String resultType) {
		return StringUtils.equalsIgnoreCase(resultType, "link");
	}

	public static Document parse(CrawlResponse response) throws Exception {

		String mimeType = response.getMimetype();
		if (StringUtils.isBlank(mimeType)) {
			mimeType = "text/html";
		}

		if (StringUtils.indexOf(mimeType, "text/html") != -1) {
			String charset = StringUtils.isBlank(response.getContentCharset()) ? "GBK" : response.getContentCharset();
			TagNode tag = htmlCleaner.clean(response.getContent(), charset);
			return new DomSerializer(htmlCleaner.getProperties()).createDOM(tag);
		}

		if (StringUtils.indexOf(mimeType, "text/xml") != -1) {
			return getDOMBuilder().parse(response.getContent());
		}

		throw new UnsupportedOperationException("unkown mimeType!");
	}

	private static DocumentBuilder getDOMBuilder() throws Exception {
		return DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
	}

}
